// BitmapSlider.cpp : implementation file
//

#include "stdafx.h"

#include "../SkinableGUIImage.h"
#include "../SkinableGUIBitmapDC.h"

#include "BitmapSlider.h"

using namespace BitmapSlider;

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
//
// CBitmapSlider v1.5
//
// It's free for everywhere - 16/September/2003 - Joon-ho Ryu
//
/////////////////////////////////////////////////////////////////////////////


CBitmapSlider::CBitmapSlider()
{
	m_nPos = m_nMin = 0;
	m_nMax = 100;
	m_nPage = 20;

	m_nMarginLeft = m_nMarginRight = m_nMarginTop = m_nMarginBottom = 0;
	m_nThumbWidth = m_nThumbHeight = 0;

	m_bChannel = m_bVertical = m_bThumb = m_bLButtonDown = FALSE;

	m_bFocusRect = m_bFocus = FALSE;
	m_bDrawFocusRect = TRUE;

	m_bEnable = TRUE;

	m_bPainterReady = FALSE;

	for(int i = 0; i < 2; i++)
	{
		m_pChannelBitmap[i] = NULL;
	}

	for(int i = 0; i < 2; i++)
	{
		m_pThumbBitmap[i] = NULL;
	}
}

CBitmapSlider::~CBitmapSlider()
{
	DestroyPainterDC();
}


BEGIN_MESSAGE_MAP(CBitmapSlider, CStatic)
	//{{AFX_MSG_MAP(CBitmapSlider)
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
	ON_WM_GETDLGCODE()
	ON_WM_KEYDOWN()
	ON_WM_KILLFOCUS()
	ON_WM_SETFOCUS()
	ON_WM_CREATE()
	ON_WM_DESTROY()
	ON_WM_SIZE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CBitmapSlider message handlers

BOOL CBitmapSlider::OnEraseBkgnd(CDC* pDC) 
{	
	// Do not erase background for the transparency effect
	return TRUE;
}

// Draw channel and thumb
//
void CBitmapSlider::OnPaint() 
{
	CPaintDC dcOrigin(this);

	GetClientRect(&m_rect);
	m_nWidth = m_rect.Width();
	m_nHeight = m_rect.Height();

	if (m_bVertical)
	{
		m_nThumbWidth = min(m_nWidth, m_pThumbImage->GetImageSize().cx);
		m_nThumbHeight = m_pThumbImage->GetImageSize().cy;
	}
	else
	{
		m_nThumbWidth = m_pThumbImage->GetImageSize().cx;
		m_nThumbHeight = min(m_nHeight, m_pThumbImage->GetImageSize().cy);
	}

	// "Flicker Free Drawing In MFC" by Keith Rule
	CMemDC dc( &dcOrigin, &m_rect, m_bTransparentChannel );

	if (!m_bPainterReady)
	{
		PreparePainterDC(&dcOrigin);
	}

	// Delete focus rectangle for transparent channel
	if( m_bFocusRect && ( m_bTransparentChannel || !m_bChannel ) ) {

		dc.DrawFocusRect( m_rect );
		m_bFocusRect = FALSE;
	}

	// Draw channel
	if( m_bChannel ) {

		// There is a bitmap for active channel
		if( m_bChannelActive && m_bEnable ) {

			// Vertical slider
			if( m_bVertical ) {
				CRect rc[2] = {
					CRect(0,Pos2Pixel(m_nPos),m_nWidth,m_nHeight),
					CRect(0, 0, m_nWidth, Pos2Pixel(m_nPos))
				};

				SkinableGUI::CSkinableGUIBitmapDC tmpDC(&dcOrigin, m_pChannelBitmap[0]);

				for(int i = 0; i < 2; i++)
				{
					tmpDC.SelectObject(m_pChannelBitmap[i]);
#ifdef _WIN32_WCE
					::TransparentBlt(dc.GetSafeHdc(),
						rc[i].left, rc[i].top,
						rc[i].Width(), rc[i].Height(),
						tmpDC.GetSafeHdc(),
						rc[i].left, rc[i].top,
						rc[i].Width(), rc[i].Height(),
						tmpDC.GetPixel(0,0));
#else
					dc.TransparentBlt(rc[i].left, rc[i].top,
						rc[i].Width(), rc[i].Height(),
						&tmpDC,
						rc[i].left, rc[i].top,
						rc[i].Width(), rc[i].Height(),
						tmpDC.GetPixel(0,0));
#endif
				}
			// Horizontal slider
			} else {
				CRect rc[] = {
					CRect(Pos2Pixel(m_nPos), 0,	m_nWidth,m_nHeight),
					CRect(0, 0,	Pos2Pixel(m_nPos), m_nHeight)
				};

				SkinableGUI::CSkinableGUIBitmapDC tmpDC(&dcOrigin, m_pChannelBitmap[0]);

				for(int i = 0; i < 2; i++)
				{
					tmpDC.SelectObject(m_pChannelBitmap[i]);
#ifdef _WIN32_WCE
					TransparentBlt(dc.GetSafeHdc(),
						rc[i].left, rc[i].top,
						rc[i].Width(), rc[i].Height(),
						tmpDC.GetSafeHdc(),
						rc[i].left, rc[i].top,
						rc[i].Width(), rc[i].Height(),
						tmpDC.GetPixel(0,0));
#else
					dc.TransparentBlt(rc[i].left, rc[i].top,
						rc[i].Width(), rc[i].Height(),
						&tmpDC,
						rc[i].left, rc[i].top,
						rc[i].Width(), rc[i].Height(),
						tmpDC.GetPixel(0,0));
#endif
				}
			}

		// Only one bitmap for channel
		} else {
			CRect rcNormal(0,0,m_nWidth, m_nHeight);

			SkinableGUI::CSkinableGUIBitmapDC tmpDC(&dcOrigin, m_pChannelBitmap[0]);
#ifdef _WIN32_WCE
			TransparentBlt(dc.GetSafeHdc(), 
				rcNormal.left, rcNormal.top,
				rcNormal.Width(), rcNormal.Height(),
				tmpDC.GetSafeHdc(),
				0, 0,
				rcNormal.Width(), rcNormal.Height(),
				tmpDC.GetPixel(0,0));
#else
			dc.TransparentBlt(rcNormal.left, rcNormal.top,
				rcNormal.Width(), rcNormal.Height(),
				&tmpDC,
				0, 0,
				rcNormal.Width(), rcNormal.Height(),
				tmpDC.GetPixel(0,0));
#endif
		}
	}

	// Draw thumb
	if( m_bThumb && m_bEnable ) {

		SkinableGUI::CSkinableGUIBitmapDC tmpDC(&dcOrigin, m_pChannelBitmap[0]);

		if( m_bThumbActive && m_bLButtonDown )
		{
			// Active thumb
			tmpDC.SelectObject(m_pThumbBitmap[1]);
		}
		else
		{
			// Normal thumb
			tmpDC.SelectObject(m_pThumbBitmap[0]);
		}

		// Vertical slider
		if( m_bVertical ) {
			int y = Pos2Pixel(m_nPos) - m_nThumbHeight / 2;

			CRect rcThumb(m_nMarginLeft + (m_nWidth - m_nThumbWidth) / 2, y,
				m_nMarginLeft + m_nThumbWidth + (m_nWidth - m_nThumbWidth) / 2,
				y + m_nThumbHeight);

#ifndef _WIN32_WCE
			dc.TransparentBlt(rcThumb.left, rcThumb.top,
				rcThumb.Width(), rcThumb.Height(),
				&tmpDC,
				0, 0,
				rcThumb.Width(), rcThumb.Height(),
				tmpDC.GetPixel(0,0));
#else
			TransparentBlt(dc.GetSafeHdc(),
				rcThumb.left, rcThumb.top,
				rcThumb.Width(), rcThumb.Height(),
				tmpDC.GetSafeHdc(),
				0, 0,
				rcThumb.Width(), rcThumb.Height(),
				tmpDC.GetPixel(0,0));
#endif
		// Horizontal slider
		} else {
			int x = Pos2Pixel(m_nPos) - m_nThumbWidth/2;

			CRect rcThumb(x , 
				m_nMarginTop + (m_nHeight - m_nThumbHeight) / 2,
				x + m_nThumbWidth,
				m_nMarginTop + m_nThumbHeight + (m_nHeight - m_nThumbHeight) / 2);

#ifdef _WIN32_WCE
			TransparentBlt(dc.GetSafeHdc(),
				rcThumb.left, rcThumb.top,
				rcThumb.Width(), rcThumb.Height(),
				tmpDC.GetSafeHdc(),
				0, 0,
				rcThumb.Width(), rcThumb.Height(),
				tmpDC.GetPixel(0,0));
#else
			dc.TransparentBlt(rcThumb.left, rcThumb.top,
				rcThumb.Width(), rcThumb.Height(),
				&tmpDC,
				0, 0,
				rcThumb.Width(), rcThumb.Height(),
				tmpDC.GetPixel(0,0));
#endif
		} // if horizontal
	} // if draw thumb

	// Draw focus rectangle
	if( m_bDrawFocusRect && m_bFocus && m_bEnable ) {

		dc.DrawFocusRect( m_rect );
		m_bFocusRect = TRUE;
	}
}

// Sets the maximum range for the slider.
//
// Parameters:
//		[IN]	nMax
//				Maximum position for the slider.
//		[IN]	bRedraw
//				TRUE to redraw after the range is set.
//				FALSE to only change maximum position.
//
void CBitmapSlider::SetRangeMax(int nMax, BOOL bRedraw)
{
	m_nMax = nMax;
	if( bRedraw )
		Invalidate();
}

// Sets the minimum range for the slider.
//
// Parameters:
//		[IN]	nMin
//				Minimum position for the slider.
//		[IN]	bRedraw
//				TRUE to redraw after the range is set.
//				FALSE to only change minimum position.
//
void CBitmapSlider::SetRangeMin(int nMin, BOOL bRedraw)
{
	m_nMin = nMin;
	if( bRedraw )
		Invalidate();
}

// Sets the range (minimum and maximum positions) for the slider.
//
// Parameters:
//		[IN]	nMin
//				Minimum position for the slider.
//		[IN]	nMax
//				Maximum position for the slider.
//		[IN]	bRedraw
//				TRUE to redraw after the range is set.
//				FALSE to only change the range.
//
void CBitmapSlider::SetRange(int nMin, int nMax, BOOL bRedraw)
{
	SetRangeMin( nMin, FALSE );
	SetRangeMax( nMax, bRedraw );
}

// Sets the current position of the slider.
//
// Parameters:
//		[IN]	nPos
//				Specifies the new slider position.
//
void CBitmapSlider::SetPos(int nPos)
{
	m_nPos = nPos;

	// Boundary check
	if( m_nPos > m_nMax )
		m_nPos = m_nMax;
	if( m_nPos < m_nMin )
		m_nPos = m_nMin;

	Invalidate();
}

// Sets the size of the page for a control.
//
// Parameters:
//		[IN]	nSize
//				The new page size of the control.
//
// Return value:
//		The previous page size.
//
int CBitmapSlider::SetPageSize(int nSize)
{
	int nRet = m_nPage;

	m_nPage = nSize;

	return nRet;
}

// Sets the left, top, right, and bottom margins for a control
//
void CBitmapSlider::SetMargin(int nLeft, int nTop, int nRight, int nBottom )
{
	SetMarginLeft( nLeft );
	SetMarginTop( nTop );
	SetMarginRight( nRight );
	SetMarginBottom( nBottom );
}

// Enables or disables control.
//
//		[IN]	bEnable
//				TRUE to enable control.
//				FALSE to disable control.
//
void CBitmapSlider::Enable(BOOL bEnable)
{
	m_bEnable = bEnable;

	// If control is disabled during dragging
	if( !m_bEnable && m_bLButtonDown ) {

		m_bLButtonDown = FALSE;
		ReleaseCapture();
	}

	Invalidate();
}

// Specify whether draw focus rectangle or not.
//
//		[IN]	bDraw
//				TRUE to draw focus rectangle.
//				FALSE to hide focus rectangle.
//
//		[IN]	bRedraw
//				TRUE to redraw status is changed.
//				FALSE to only change the status.
//
void CBitmapSlider::DrawFocusRect(BOOL bDraw, BOOL bRedraw)
{
	m_bDrawFocusRect = bDraw;

	if( bRedraw )
		Invalidate();
}

// OnLButtonDown
//
// Dragging is started
//
void CBitmapSlider::OnLButtonDown(UINT nFlags, CPoint point) 
{
	if( !m_bEnable )
		return;

	SetCapture();
	SetFocus();

	m_bLButtonDown = TRUE;

	// If mouse button is clicked on the thumb,
	// capture the coordinates of mouse pointer and center of thumb
	// and calculate distance between them.
	if( m_bVertical ) {

		if( abs( point.y - Pos2Pixel( m_nPos ) ) <= m_nThumbHeight / 2 )
			m_nMouseOffset = point.y - Pos2Pixel( m_nPos );
		else
			m_nMouseOffset = 0;

	} else {

		if( abs( point.x - Pos2Pixel( m_nPos ) ) <= m_nThumbWidth / 2 )
			m_nMouseOffset = point.x - Pos2Pixel( m_nPos );
		else
			m_nMouseOffset = 0;
	}

	OnMouseMove( nFlags, point );
	Invalidate();

	CStatic::OnLButtonDown(nFlags, point);
}

// OnMouseMove
//
// During dragging
//
void CBitmapSlider::OnMouseMove(UINT nFlags, CPoint point) 
{
	if( !m_bLButtonDown || !m_bEnable )
		return;

	int nPixel;

	// Boundary check
	if( m_bVertical ) {

		nPixel = point.y - m_nMouseOffset;

		if( nPixel > m_nHeight - m_nMarginBottom - m_nThumbHeight/2 )
			nPixel = m_nHeight - m_nMarginBottom - m_nThumbHeight/2;

		if( nPixel < m_nMarginTop + m_nThumbHeight/2 )
			nPixel = m_nMarginTop + m_nThumbHeight/2;

	} else {

		nPixel = point.x - m_nMouseOffset;

		if( nPixel < m_nMarginLeft + m_nThumbWidth/2 )
			nPixel = m_nMarginLeft + m_nThumbWidth/2;

		if( nPixel > m_nWidth - m_nMarginRight - m_nThumbWidth/2 )
			nPixel = m_nWidth - m_nMarginRight - m_nThumbWidth/2;
	}

	// Apply change
	if( Pos2Pixel(m_nPos) != nPixel ) {

		SetPos( Pixel2Pos( nPixel ) );

		::PostMessage(
			GetParent()->GetSafeHwnd(), WM_BITMAPSLIDER_MOVING,
			GetDlgCtrlID(), m_nPos );
	}

	CStatic::OnMouseMove(nFlags, point);
}

// OnLButtonUp
//
// Dragging is finished
//
void CBitmapSlider::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if( !m_bEnable )
		return;

	ReleaseCapture();
	m_bLButtonDown = FALSE;

	Invalidate();

	::PostMessage(
		GetParent()->GetSafeHwnd(),	WM_BITMAPSLIDER_MOVED,
		GetDlgCtrlID(), m_nPos );

	CStatic::OnLButtonUp(nFlags, point);
}

// Calculate point of thumb from position value
//
int CBitmapSlider::Pos2Pixel(int nPos)
{
	if( m_bVertical ) {

		return
			m_nMarginTop + m_nThumbHeight/2 +
			(int)(
			( m_nHeight - m_nMarginTop - m_nMarginBottom - m_nThumbHeight ) *
			((double) ( nPos - m_nMin ) / ( m_nMax - m_nMin ) )
			);

	} else {

		return (int)(
			( m_nWidth - m_nMarginLeft - m_nMarginRight - m_nThumbWidth ) *
			((double) ( nPos - m_nMin ) / ( m_nMax - m_nMin ) )
			) + m_nMarginLeft + m_nThumbWidth/2;
	}
}

// Calculate position value from point of mouse
//
int CBitmapSlider::Pixel2Pos(int nPixel)
{
	if( m_bVertical ) {

		return (int)(
			m_nMin +
			(double)( nPixel - m_nMarginTop - m_nThumbHeight/2) /
			( m_nHeight - m_nMarginBottom - m_nMarginTop - m_nThumbHeight ) *
			( m_nMax - m_nMin )
			);

	} else {

		return (int)(
			m_nMin +
			(double)( nPixel - m_nMarginLeft - m_nThumbWidth/2 ) /
			( m_nWidth - m_nMarginLeft - m_nMarginRight - m_nThumbWidth ) *
			( m_nMax - m_nMin )
			);
	}
}

// To get keyboard input
//
UINT CBitmapSlider::OnGetDlgCode() 
{
	if( GetKeyState(VK_TAB) >= 0 ) {

		return  DLGC_WANTALLKEYS;
	}
	
	return CStatic::OnGetDlgCode();
}

// Handling keyboard input
//
void CBitmapSlider::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	if( !m_bEnable )
		return;

	switch( nChar ) {

	// Left & up
	case VK_LEFT :
	case VK_UP :

		SetPos( m_nPos-1 );
		break;

	// Right & down
	case VK_RIGHT :
	case VK_DOWN :

		SetPos( m_nPos+1 );
		break;

	// Home
	case VK_HOME :

		SetPos( m_nMin );
		break;

	// End
	case VK_END :

		SetPos( m_nMax );
		break;

	// Page up
	case VK_PRIOR :

		SetPos( m_nPos - m_nPage );
		break;

	// Page down
	case VK_NEXT :

		SetPos( m_nPos + m_nPage );
		break;

	default :

		CStatic::OnKeyDown(nChar, nRepCnt, nFlags);
		return;
	}
	
	::PostMessage(
		GetParent()->GetSafeHwnd(),	WM_BITMAPSLIDER_MOVED,
		GetDlgCtrlID(), m_nPos );

	CStatic::OnKeyDown(nChar, nRepCnt, nFlags);
}

// Control looses its focus
//
void CBitmapSlider::OnKillFocus(CWnd* pNewWnd) 
{
	CStatic::OnKillFocus(pNewWnd);

	m_bFocus = FALSE;
	Invalidate();
}

// This control gains its focus
//
void CBitmapSlider::OnSetFocus(CWnd* pOldWnd) 
{
	CStatic::OnSetFocus(pOldWnd);

	m_bFocus = TRUE;
	Invalidate();
}

// Release resources
//
void CBitmapSlider::OnDestroy() 
{
	CStatic::OnDestroy();

	DestroyPainterDC();
}

void CBitmapSlider::SetImages(SkinableGUI::CSkinableGUIImage * pChannelImage,
		SkinableGUI::CSkinableGUIImage * pThumbImage,
		SkinableGUI::CSkinableGUIImage * pChannelActiveImage,
		SkinableGUI::CSkinableGUIImage * pThumbActiveImage)
{
	m_pChannelImage = pChannelImage;
	m_pThumbImage= pThumbImage;
	m_pChannelActiveImage = pChannelActiveImage == NULL ?
		pChannelImage : pChannelActiveImage;
	m_pThumbActiveImage = pThumbActiveImage == NULL ?
		pThumbImage : pThumbActiveImage;
	m_bChannel = pChannelImage != NULL;
	m_bThumb = pThumbImage != NULL;
	m_bChannelActive = pChannelActiveImage != NULL;
	m_bThumbActive = pThumbActiveImage != NULL;
}

void CBitmapSlider::OnSize(UINT nType, int cx, int cy)
{
	CStatic::OnSize(nType, cx, cy);

	DestroyPainterDC();
}

void CBitmapSlider::PreparePainterDC(CDC * pDC)
{
	SkinableGUI::CSkinableGUIImage * pImage = m_pChannelImage;

	CDC tmpDC;

	tmpDC.CreateCompatibleDC(pDC);

	CBitmap * pOldBitmap = NULL;

	for(int i = 0; i < 2; i++)
	{
		m_pChannelBitmap[i] = new CBitmap();
		m_pChannelBitmap[i]->CreateCompatibleBitmap(pDC,
			m_nWidth, m_nHeight);

		if (pOldBitmap == NULL)
		{
			pOldBitmap =
				tmpDC.SelectObject(m_pChannelBitmap[i]);
		}
		else
		{
			tmpDC.SelectObject(m_pChannelBitmap[i]);
		}

		pImage->Blt(&tmpDC,
			CRect(0,0,m_nWidth, m_nHeight));

		pImage = m_pChannelActiveImage;
	}

	pImage = m_pThumbImage;

	for(int i = 0; i < 2; i++)
	{
		m_pThumbBitmap[i] = new CBitmap();
		m_pThumbBitmap[i]->CreateCompatibleBitmap(pDC,
			m_nThumbWidth, m_nThumbHeight);

		tmpDC.SelectObject(m_pThumbBitmap[i]);

		pImage->Blt(&tmpDC,
			CRect(0,0,m_nThumbWidth, m_nThumbHeight));

		pImage = m_pThumbActiveImage;
	}

	tmpDC.SelectObject(pOldBitmap);

	m_bPainterReady = TRUE;
}

void CBitmapSlider::DestroyPainterDC()
{
	if (!m_bPainterReady)
		return;

	for(int i = 0; i < 2; i++)
	{
		if (m_pChannelBitmap[i] != NULL)
		{
			delete m_pChannelBitmap[i];
		}
	}

	for(int i = 0; i < 2; i++)
	{
		if (m_pThumbBitmap[i] != NULL)
		{
			m_pThumbBitmap[i]->DeleteObject();
			delete m_pThumbBitmap[i];
		}
	}

	m_bPainterReady = FALSE;
}

